package laboratoryQ.experiment.reportage;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.CellEditorListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;

import laboratoryQ.experiment.IExperimentResult;
import laboratoryQ.experiment.IExperiment.IExpRunStatusHandler;
import laboratoryQ.experiment.settings.IExperimentSettings;
import laboratoryQ.experiment.settings.ISettingsContainer;
import laboratoryQ.experiment.settings.ReflectionBasedSettingsContainer;
import laboratoryQ.experiment.settings.ISettingsContainer.ISettingsItem;
import laboratoryQ.experiment.ui.reportage.IExperimentResult2componentConverter;
import laboratoryQ.experiment.ui.reportage.IReportageModel;

class IncDecPropValActionListener implements ActionListener {
	private int incDecMul;
	private int position;
	private ExperimentRes2ComponentBasedReportageModel e2gReportageModel;
	
	public IncDecPropValActionListener(
			int incDecMul, 
			int position,
			ExperimentRes2ComponentBasedReportageModel e2gReportageModel
	) {
		this.incDecMul = incDecMul;
		this.position = position;
		this.e2gReportageModel = e2gReportageModel;
	}

	@Override
	public void actionPerformed(ActionEvent event) {
		ISettingsItem item = e2gReportageModel.getSettingsContainer().listItems().get(position);
		if (incDecMul > 0) {
			item.increase();
		} else {
			item.decrease();
		}
		e2gReportageModel.refreshTable(position);
	}
}

public class ExperimentRes2ComponentBasedReportageModel implements IReportageModel
		, TableModel
		, TableCellRenderer
		, TableCellEditor
		, IExpRunStatusHandler 
{
	private static final List<JComponent> NO_RESULTS_VIEW_COMPONENTS;
	private static final String NOT_RAN_YET = "Not ran yet.";

	static { System.out.println("\nSMALL BLOCKING move 2 package laboratoryQ.experiment.ui.reportage + delete this package"); }
	
	static {
		NO_RESULTS_VIEW_COMPONENTS = new ArrayList<JComponent>(1);
		NO_RESULTS_VIEW_COMPONENTS.add(new JLabel("Not ran yet."));
	}
	
	private ISettingsContainer settingsContainer;
	private IExperimentResult2componentConverter result2componentConverter;
	
	private JTable setupSettingsTable;
	private List<JComponent> viewComponents = NO_RESULTS_VIEW_COMPONENTS;

	public ExperimentRes2ComponentBasedReportageModel(
			IExperimentSettings experimentSettings,
			IExperimentResult2componentConverter result2diagramConverter
	) {
		this(
				new ReflectionBasedSettingsContainer(experimentSettings), 
				result2diagramConverter
		);
	}

	public ExperimentRes2ComponentBasedReportageModel(
			ISettingsContainer settingsContainer,
			IExperimentResult2componentConverter result2diagramConverter
	) {
		this.settingsContainer = settingsContainer;
		this.result2componentConverter = result2diagramConverter;
		initialiseSetupSettingsTable();
	}

	public ISettingsContainer getSettingsContainer() {
		return settingsContainer;
	}

	private void initialiseSetupSettingsTable() {
		setupSettingsTable = new JTable();
		setupSettingsTable.setModel(this);
		int NORMAL_TABLE_WIDTH = 200;
		int BUTTON_TABLE_WIDTH = 45;
		for (int i = 0; i < TABLE_CLASSES.length; i++) {
			int tableWidth = NORMAL_TABLE_WIDTH;
			if (TABLE_CLASSES[i] == JButton.class) {
				setupSettingsTable.getColumnModel().getColumn(i).setCellRenderer(this);
				setupSettingsTable.getColumnModel().getColumn(i).setCellEditor(this);
				tableWidth = BUTTON_TABLE_WIDTH;
			}
			setupSettingsTable.getColumnModel().getColumn(i).setMaxWidth(tableWidth);
		}
		initialiseButtons();
	}

	@Override
	public String getExperimentTitle() {
		return result2componentConverter.getExperimentTitle();
	}

	@Override
	public JComponent getSetupSettingsComponent() {
		return new JScrollPane(setupSettingsTable);
	}

	@Override
	public JComponent getViewComponentAt(int position) {
		return viewComponents.get(position);
	}

	@Override
	public String getViewTitleAt(int position) {
		if (viewComponents == NO_RESULTS_VIEW_COMPONENTS) {
			return NOT_RAN_YET;
		}
		return result2componentConverter.getViewTitleAt(position);
	}

	@Override
	public int getViewsCount() {
		return viewComponents.size();
	}

	@Override
	public void shutdown() {
	}
	
	@Override
	public IExpRunStatusHandler getExperimentResultHandler() {
		return this;
	}

	@Override
	public IExperimentSettings getSettings() {
		return settingsContainer.getSettings();
	}
	
	@Override
	public void onExperimentRunStart() {
		result2componentConverter.onExperimentRunStart(viewComponents);
	}
	
	// IExpRunStatusHandler
	@Override
	public void handleExperimentResult(IExperimentResult expResult) {
		viewComponents = result2componentConverter
			.convert2component(expResult);
	}

	@Override
	public void stepsProcessed(int processed, int total) {
	}

	// TableModel
	private Set<TableModelListener> tableListeners = new HashSet<TableModelListener>();
	private JButton[][] tableButtons;
	@SuppressWarnings("unchecked")
	private static final Class[] TABLE_CLASSES = new Class[] {
			String.class,
			JButton.class,
			String.class,
			JButton.class,
	};
	String[] TABLE_NAMES = new String[] {
			"Property",
			"<",
			"Value",
			">",
	};

	private void initialiseButtons() {
		int rowsCount = getRowCount();
		tableButtons = new JButton[rowsCount][2];
		for (int i = 0; i < rowsCount; i++) {
			if (settingsContainer.listItems().get(i).isIncreasable()) {
				tableButtons[i][0] = new JButton("<");
				tableButtons[i][0].addActionListener(new IncDecPropValActionListener(-1, i, this));
				tableButtons[i][1] = new JButton(">"); 
				tableButtons[i][1].addActionListener(new IncDecPropValActionListener(1, i, this));
			}
		}
	}
	
	@Override
	public void addTableModelListener(TableModelListener listener) {
		tableListeners.add(listener);
	}
	
	@Override
	public void removeTableModelListener(TableModelListener listener) {
		tableListeners.remove(listener);
	}

	@Override
	public Class<?> getColumnClass(int col) {
		return TABLE_CLASSES[col];
	}

	@Override
	public int getColumnCount() {
		return TABLE_CLASSES.length;
	}

	@Override
	public String getColumnName(int col) {
		return TABLE_NAMES[col];
	}

	@Override
	public int getRowCount() {
		return settingsContainer.listItems().size();
	}

	@Override
	public Object getValueAt(int row, int col) {
		if (col % 2 == 1) {
			return tableButtons[row][col / 2];
		}
		if (col == 0)
		{
			return settingsContainer.listItems().get(row).getName();
		}
		if (col == 2)
		{
			return settingsContainer.listItems().get(row).getValue().toString();
		}
		return null;
	}

	@Override
	public boolean isCellEditable(int row, int col) {
		return col > 0;
	}

	@Override
	public void setValueAt(Object value, int row, int col) {
		settingsContainer.listItems().get(row).setValue(value);
	}

	public void refreshTable(int row) {
		for (TableModelListener listener : tableListeners) {
			TableModelEvent tme = new TableModelEvent(this, row, row);
			listener.tableChanged(tme);
		}
	}
	
	// TableCellRenderer
	@Override
	public JComponent getTableCellRendererComponent(JTable table, Object button, boolean b1, boolean b2, int row, int col) {
		return (JButton)button;
	}

	// TableCellEditor
	@Override
	public JComponent getTableCellEditorComponent(JTable table, Object button, boolean b, int row, int col) {
		return (JButton)button;
	}
	@Override
	public void addCellEditorListener(CellEditorListener arg0) {}
	@Override
	public void cancelCellEditing() {}
	@Override
	public Object getCellEditorValue() { return null; }
	@Override
	public boolean isCellEditable(EventObject arg0) { return true; }
	@Override
	public void removeCellEditorListener(CellEditorListener arg0) {}
	@Override
	public boolean shouldSelectCell(EventObject arg0) { return true; }
	@Override
	public boolean stopCellEditing() { return true; }
}
